/**
 * @Date: Mar 3, 2010 2:30:33 PM
 */
package com.philip.journal.core.bean;

import java.lang.reflect.Method;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.philip.journal.common.BeanUtils;

/**
 * @author cry30
 */
public abstract class AbstractAuditableBean extends AbstractBean {

    /** Helper field for the given attribute. */
    public static final String CREATE_DATE = "createDate";
    /** Helper field for the given attribute. */
    public static final String CREATE_TIME = "createTime";
    /** Helper field for the given attribute. */
    public static final String UPDATE_DATE = "updateDate";
    /** Helper field for the given attribute. */
    public static final String UPDATE_TIME = "updateTime";
    /** Helper field for the given attribute. */
    public static final String CREATOR = "creator";
    /** Helper field for the given attribute. */
    public static final String UPDATER = "updater";

    /** RTFC. */
    private User creator;

    /** Last person to do the modification. */
    private User updater;

    /** RTFC. */
    private Date createDate;
    /** RTFC. */
    private Date createTime;
    /** RTFC. */
    private Date updateDate;
    /** RTFC. */
    private Date updateTime;

    /** Sub class reusable logger. */
    private final transient Log logger = LogFactory.getLog(getClass());//NOPMD javadoc.

    /**
     * Sub class should identify which field stands as primary.
     *
     * @return the primary key field attribute name.
     */
    public abstract String getPrimaryKeyField();

    /**
     * Accessor method for {@link #creator}.
     *
     * @return {@link #creator} of this entity.
     */
    public User getCreator()
    {
        return this.creator;
    }

    /**
     * @param pCreator new {@link #creator} to set.
     */
    public void setCreator(final User pCreator)
    {
        this.creator = pCreator;
    }

    /**
     * Accessor method for {@link #updater}.
     *
     * @return {@link #updater} of this entity.
     */
    public User getUpdater()
    {
        return this.updater;
    }

    /**
     * @param pUpdater new {@link #updater} to set.
     */
    public void setUpdater(final User pUpdater)
    {
        this.updater = pUpdater;
    }

    /**
     * Accessor method for {@link #createDate}.
     *
     * @return {@link #createDate} of this entity.
     */
    public Date getCreateDate()
    {
        return this.createDate;
    }

    /**
     * @param pCreateDate new {@link #createDate} to set.
     */
    public void setCreateDate(final Date pCreateDate)
    {
        this.createDate = pCreateDate;
    }

    /**
     * Accessor method for {@link #createTime}.
     *
     * @return {@link #createTime} of this entity.
     */
    public Date getCreateTime()
    {
        return this.createTime;
    }

    /**
     * @param pCreateTime new {@link #createTime} to set.
     */
    public void setCreateTime(final Date pCreateTime)
    {
        this.createTime = pCreateTime;
    }

    /**
     * Accessor method for {@link #updateDate}. Will return the create time if the entity has never been updated.
     *
     * @return {@link #updateDate} of this entity.
     */
    public Date getUpdateDate()
    {
        Date retval = updateDate;
        if (updateDate == null) {
            retval = createDate;
        }
        return retval;
    }

    /**
     * @param pUpdateDate new {@link #updateDate} to set.
     */
    public void setUpdateDate(final Date pUpdateDate)
    {
        this.updateDate = pUpdateDate;
    }

    /**
     * Will return the create time if last update time is not present.
     *
     * @return last update time.
     */
    public Date getUpdateTime()
    {
        return this.updateTime == null ? this.createTime : this.updateTime;
    }

    /**
     * Accessor method.
     *
     * @param pUpdateTime update time to set.
     */
    public void setUpdateTime(final Date pUpdateTime)
    {
        this.updateTime = pUpdateTime;
    }

    @Override
    public final String toString()
    {
        final StringBuilder strBuilder = new StringBuilder();
        strBuilder.append("\n" + getClass() + "\n-------------------\n");

        final Object[] emptyObj = {};
        if (getClass().getName().indexOf("$$EnhancerByMockitoWithCGLIB$$") == -1) {
            for (final Method method : getClass().getMethods()) {
                final String methodName = method.getName();
                if (methodName.startsWith("get") && !"getClass".equals(methodName) && !"getLogger".equals(methodName)) {
                    processMethod(strBuilder, method, emptyObj);
                }
            }
        }
        strBuilder.append("\n");
        return strBuilder.toString();
    }

    /**
     * Refactored out of {@link #toString()}.
     *
     * @param strBuilder StringBuilder instance.
     * @param method Method instance.
     * @param emptyObj optimized reused empty object.
     */
    void processMethod(final StringBuilder strBuilder, final Method method, final Object[] emptyObj)
    {
        strBuilder.append(BeanUtils.getPropertyName(method));
        strBuilder.append("=[");
        if (method.getReturnType().isPrimitive()
                || method.getReturnType().getPackage().toString().startsWith("package java")) {
            try {
                strBuilder.append(method.invoke(this, emptyObj));
            } catch (final Exception swallow) {
                if (logger != null) {
                    logger.warn(swallow.getMessage(), swallow);
                }
            }
        } else {
            Object val = null;
            try {
                val = method.invoke(this, emptyObj);
            } catch (final Exception ignore) {
                if (logger != null) {
                    logger.warn(ignore.getMessage(), ignore);
                }
            }
            strBuilder.append(val == null ? val : "Object");//FB: This is intended.
        }
        strBuilder.append("]\n");

    }
}
